package edu.usm.roberge.usga;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import edu.usm.roberge.HttpRequest;
import edu.usm.roberge.UsgaCourse;

/**
 * Encapsulate requests to USGA golf course database
 * Their site contains a database of all registered USGA golf courses
 * Unfortunately, they use an AJAX interface so the scraping is not done 
 * with a single request.
 * 
 * They are using ASP on their backend servers and fortunately the way
 * ASP implements AJAX allows scraping. The work flow is as follows:
 * - Make initial request to page. This is required because their page
 * that lists all courses requires 2 parameters (__VIEWSTATE and __EVENTVALIDATION).
 * These are generated by the ASP framework for each page request. Without these
 * the server will not respond with the golf courses. This first request
 * is solely for the purpose of extracting those 2 parameters. Then, using those
 * 2 parameters, you can retrieve the golf courses by sending another request
 * with ddState and txtClubCity parameters
 * 
 * @author Dave Roberge
 *
 */
public class UsgaCourseListRequest {
	private final static Logger logger = Logger.getLogger(UsgaCourseListRequest.class.getName());
	
	private final static String URL = "http://ncrdb.usga.org/NCRDB/NCRListing.aspx";
	private static final String URL2 = "http://ncrdb.usga.org/NCRDB/courseTeeInfo.aspx?CourseID=";
	
	// form parameters
	private final static String VIEW_STATE_PARAM = "__VIEWSTATE";
	private final static String EVENT_VALIDATION = "__EVENTVALIDATION";
	private final static String STATE_PARAM = "ddState";
	private final static String CITY_PARAM = "txtClubCity";
	
	// regex's targetted to retrieving the required VIEWSTATE and EVENTVALIDATION parameters
	private final static Pattern VIEW_STATE_REG_EX = Pattern.compile("id=\"__VIEWSTATE\" value=\"([^\"]*)\"");
	private final static Pattern EVENT_VALIDATION_REG_EX = Pattern.compile("id=\"__EVENTVALIDATION\" value=\"([^\"]*)\"");
	
	// regex's aimed at extracting the course name and course id from the pages html structure
	private final static Pattern COURSE_TABLE_REG_EX = Pattern.compile("<table(?:.*)id=\"gvCourses\"[^>]*>(.*?)</table>");
	private final static Pattern TABLE_CELL_REG_EX = Pattern.compile("<td[^>]*>(.*?)</td>");
	private final static Pattern ANCHOR_LINK_REG_EX = Pattern.compile("CourseID=([\\d]+)\">(.*?)</a>");
	
	public List<UsgaCourse> getCoursesByCity(String city, String state) {
		HttpRequest req = new HttpRequest(URL);
		String resp = req.makeRequest();
		String eventId = getEventId(resp);
		String viewState = getViewState(resp);
		
		req = new HttpRequest(URL);
		resp = req.setPostRequest()
				  .addParameter(VIEW_STATE_PARAM, viewState)
				  .addParameter(EVENT_VALIDATION, eventId)
				  .addParameter(CITY_PARAM, city)
				  .addParameter(STATE_PARAM, state)
				  .makeRequest();
		
		return parseCourses(resp);
	}

	private List<UsgaCourse> parseCourses(String resp) {
		List<UsgaCourse> courseList = new ArrayList<UsgaCourse>();
		// strip all tabs/new lines/carriage returns
		resp = resp.replaceAll("[\n\r\t]", "");
		// extract the table element that contains the courses
		Matcher matcher = COURSE_TABLE_REG_EX.matcher(resp);
		if (!matcher.find()) {
			return null;
		}
		String courses = matcher.group(1);
		
		// extract course name and course id
		matcher = TABLE_CELL_REG_EX.matcher(courses);
		// the content is in the 2nd column of the table
		int currentColumn = 0;
		while(matcher.find()) {
			if (currentColumn == 1) {
				courseList.add(extractCourse(matcher.group(1)));
			} 

			if (currentColumn == 3) {
				currentColumn = 0;
			} else {
				currentColumn++;
			}
		}
		return courseList;
	}

	private UsgaCourse extractCourse(String group) {
		Matcher matcher = ANCHOR_LINK_REG_EX.matcher(group);
		if (matcher.find()) {
			return new UsgaCourse(matcher.group(2), matcher.group(1));
		}
		logger.severe("The regex to extract the course from the anchor was not right.");
		return null;
	}

	private String getViewState(String resp) {
		Matcher matcher = VIEW_STATE_REG_EX.matcher(resp);
		String viewState = null;
		if (matcher.find() && matcher.groupCount() == 1) {
			viewState = matcher.group(1);
		} else {
			logger.severe("Could not find the viewState parameter on the page");
		}
		
		return viewState;
	}

	private String getEventId(String resp) {
		Matcher matcher = EVENT_VALIDATION_REG_EX.matcher(resp);
		String eventValidation = null;
		if (matcher.find() && matcher.groupCount() == 1) {
			eventValidation = matcher.group(1);
		}
		
		return eventValidation;
	}
}
